package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"os"
	"strings"
)

type CompileCommand struct {
	Directory string `json:"directory"`
	Command   string `json:"command"`
	File      string `json:"file"`
}

func main() {
	if len(os.Args) <= 1 {
		panic("compile_commands.json not specified")
	}
	data, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		panic(err)
	}
	var cmds []CompileCommand
	if err = json.Unmarshal(data, &cmds); err != nil {
		panic(err)
	}
	replaceCCache(cmds)
	replaceImacros(cmds)
	data, err = json.MarshalIndent(cmds, "", "  ")
	if err != nil {
		panic(err)
	}
	if err := ioutil.WriteFile(os.Args[1], data, 0500); err != nil {
		panic(err)
	}
}

func replaceCCache(cmds []CompileCommand) {
	for i, cmd := range cmds {
		if index := strings.Index(cmd.Command, "ccache "); index != -1 {
			cmds[i].Command = cmd.Command[index+len("ccache "):]
		}
	}
}

func replaceImacros(cmds []CompileCommand) {
	imacros := map[string]string{}
	for i := range cmds {
		for true {
			cmd := cmds[i]
			if !strings.Contains(cmd.Command, "-imacros ") {
				break
			}
			index := strings.Index(cmd.Command, "-imacros ")
			fileStart := index + len("-imacros ")
			nextSpace := strings.Index(cmd.Command[fileStart:], " ")
			var fileEnd int
			if nextSpace == -1 { // -imacros is the last para
				fileEnd = len(cmd.Command)
			} else {
				fileEnd = fileStart + nextSpace
			}
			fileName := cmd.Command[fileStart:fileEnd]
			if defines, ok := imacros[fileName]; ok {
				cmds[i].Command = cmd.Command[:index] + defines + cmd.Command[fileEnd:]
			} else {
				defines = imacroFile2Defines(fileName)
				cmds[i].Command = cmd.Command[:index] + defines + cmd.Command[fileEnd:]
			}
		}
	}
}

func imacroFile2Defines(fileName string) string {
	data, err := ioutil.ReadFile(fileName)
	if err != nil {
		panic(err)
	}
	dataRows := bytes.Split(data, []byte{'\n'})
	var sb strings.Builder
	for _, row := range dataRows {
		items := strings.Fields(string(row))
		if len(items) == 0 {
			continue
		}
		sb.WriteString("-D")
		sb.WriteString(items[1])
		if len(items) > 2 {
			sb.WriteString("=")
			sb.WriteString(items[2])
		}
		sb.WriteString(" ")
	}
	return strings.TrimSpace(sb.String())
}
